package com.cloudbees.jenkins.plugins.sshagent;
import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
import hudson.model.Fingerprint;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.model.Result;
import hudson.tasks.Shell;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.BuildWatcher;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsCollectionContaining.hasItem;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.hamcrest.core.IsNull.nullValue;
public class SSHAgentBuildWrapperTest extends SSHAgentBase {
@Rule
public JenkinsRule r = new JenkinsRule();
@ClassRule
public static BuildWatcher buildWatcher = new BuildWatcher();
@Test
public void sshAgentAvailable() throws Exception {
startMockSSHServer();
List<String> credentialIds = new ArrayList<String>();
credentialIds.add(CREDENTIAL_ID);
SSHUserPrivateKey key = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, credentialIds.get(0), "cloudbees",
new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(getPrivateKey()), "cloudbees", "test");
SystemCredentialsProvider.getInstance().getCredentials().add(key);
SystemCredentialsProvider.getInstance().save();
FreeStyleProject job = r.createFreeStyleProject();
job.setAssignedNode(r.createSlave());
SSHAgentBuildWrapper sshAgent = new SSHAgentBuildWrapper(credentialIds, false);
job.getBuildWrappersList().add(sshAgent);
Shell shell = new Shell("set | grep SSH_AUTH_SOCK "
+ "&& ssh-add -l "
+ "&& ssh -o NoHostAuthenticationForLocalhost=yes -o StrictHostKeyChecking=no -p " + getAssignedPort()
+ " -v -l cloudbees " + SSH_SERVER_HOST);
job.getBuildersList().add(shell);
r.assertBuildStatusSuccess(job.scheduleBuild2(0));
stopMockSSHServer();
}
@Test
public void sshAgentDoesNotDieAfterFirstUse() throws Exception {
startMockSSHServer();
List<String> credentialIds = new ArrayList<String>();
credentialIds.add(CREDENTIAL_ID);
SSHUserPrivateKey key = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, credentialIds.get(0), "cloudbees",
new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(getPrivateKey()), "cloudbees", "test");
SystemCredentialsProvider.getInstance().getCredentials().add(key);
SystemCredentialsProvider.getInstance().save();
FreeStyleProject job = r.createFreeStyleProject();
SSHAgentBuildWrapper sshAgent = new SSHAgentBuildWrapper(credentialIds, false);
job.getBuildWrappersList().add(sshAgent);
Shell shell = new Shell(
"set | grep SSH_AUTH_SOCK "
+ "&& ssh-add -l "
+ "&& ssh -o NoHostAuthenticationForLocalhost=yes -o StrictHostKeyChecking=no -p "
+ getAssignedPort() + " -v -l cloudbees " + SSH_SERVER_HOST);
job.getBuildersList().add(shell);
shell = new Shell("set | grep SSH_AUTH_SOCK "
+ "&& ssh-add -l "
+ "&& ssh -o NoHostAuthenticationForLocalhost=yes -o StrictHostKeyChecking=no -p "
+ getAssignedPort() + " -v -l cloudbees " + SSH_SERVER_HOST);
job.getBuildersList().add(shell);
shell = new Shell("set | grep SSH_AUTH_SOCK "
+ "&& ssh-add -l "
+ "&& ssh -o NoHostAuthenticationForLocalhost=yes -o StrictHostKeyChecking=no -p "
+ getAssignedPort() + " -v -l cloudbees " + SSH_SERVER_HOST);
job.getBuildersList().add(shell);
r.assertBuildStatusSuccess(job.scheduleBuild2(0));
stopMockSSHServer();
}
@Test
public void sshAgentUnavailable() throws Exception {
startMockSSHServer();
List<String> credentialIds = new ArrayList<String>();
credentialIds.add(CREDENTIAL_ID);
SSHUserPrivateKey key = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, credentialIds.get(0), "cloudbees",
new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(getPrivateKey()), "cloudbees", "test");
SystemCredentialsProvider.getInstance().getCredentials().add(key);
SystemCredentialsProvider.getInstance().save();
FreeStyleProject job = r.createFreeStyleProject();
Shell shell = new Shell("ssh -o StrictHostKeyChecking=no -p " + getAssignedPort() + " -v -l cloudbees " + SSH_SERVER_HOST);
job.getBuildersList().add(shell);
r.assertLogContains("Permission denied (publickey).", r.assertBuildStatus(Result.FAILURE, job.scheduleBuild2(0).get()));
stopMockSSHServer();
}
@Test
public void sshAgentWithInvalidCredentials() throws Exception {
startMockSSHServer();
List<String> credentialIds = new ArrayList<String>();
credentialIds.add(CREDENTIAL_ID);
SSHUserPrivateKey key = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, credentialIds.get(0), "cloudbees",
new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(getPrivateKey()), "BAD-passphrase-cloudbees", "test");
SystemCredentialsProvider.getInstance().getCredentials().add(key);
SystemCredentialsProvider.getInstance().save();
FreeStyleProject job = r.createFreeStyleProject();
SSHAgentBuildWrapper sshAgent = new SSHAgentBuildWrapper(credentialIds, false);
job.getBuildWrappersList().add(sshAgent);
Shell shell = new Shell("ssh -o StrictHostKeyChecking=no -p " + getAssignedPort() + " -v -l cloudbees " + SSH_SERVER_HOST);
job.getBuildersList().add(shell);
r.assertLogContains("Failed to run ssh-add", r.assertBuildStatus(Result.FAILURE, job.scheduleBuild2(0).get()));
stopMockSSHServer();
}
@Issue("JENKINS-38830")
@Test
public void testTrackingOfCredential() throws Exception {
startMockSSHServer();
List<String> credentialIds = new ArrayList<String>();
credentialIds.add(CREDENTIAL_ID);
SSHUserPrivateKey key = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, credentialIds.get(0), "cloudbees",
new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(getPrivateKey()), "cloudbees", "test");
SystemCredentialsProvider.getInstance().getCredentials().add(key);
SystemCredentialsProvider.getInstance().save();
Fingerprint fingerprint = CredentialsProvider.getFingerprintOf(key);
assertThat("No fingerprint created until first use", fingerprint, nullValue());
FreeStyleProject job = r.createFreeStyleProject();
SSHAgentBuildWrapper sshAgent = new SSHAgentBuildWrapper(credentialIds, false);
job.getBuildWrappersList().add(sshAgent);
Shell shell = new Shell("set | grep SSH_AUTH_SOCK "
+ "&& ssh-add -l "
+ "&& ssh -o NoHostAuthenticationForLocalhost=yes -o StrictHostKeyChecking=no -p " + getAssignedPort()
+ " -v -l cloudbees " + SSH_SERVER_HOST);
job.getBuildersList().add(shell);
r.assertBuildStatusSuccess(job.scheduleBuild2(0));
fingerprint = CredentialsProvider.getFingerprintOf(key);
assertThat(fingerprint, notNullValue());
assertThat(fingerprint.getJobs(), hasItem(is(job.getFullName())));
Fingerprint.RangeSet rangeSet = fingerprint.getRangeSet(job);
assertThat(rangeSet, notNullValue());
assertThat(rangeSet.includes(job.getLastBuild().getNumber()), is(true));
stopMockSSHServer();
}
@Issue("JENKINS-42093")
@Test
public void sshAgentWithSpacesInWorkspacePath() throws Exception {
startMockSSHServer();
List<String> credentialIds = new ArrayList<String>();
credentialIds.add(CREDENTIAL_ID);
SSHUserPrivateKey key = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, credentialIds.get(0), "cloudbees",
new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(getPrivateKey()), "cloudbees", "test");
SystemCredentialsProvider.getInstance().getCredentials().add(key);
SystemCredentialsProvider.getInstance().save();
FreeStyleProject job = r.createFreeStyleProject("name with spaces");
job.setAssignedNode(r.createSlave());
SSHAgentBuildWrapper sshAgent = new SSHAgentBuildWrapper(credentialIds, false);
job.getBuildWrappersList().add(sshAgent);
Shell shell = new Shell("set | grep SSH_AUTH_SOCK "
+ "&& ssh-add -l "
+ "&& ssh -o NoHostAuthenticationForLocalhost=yes -o StrictHostKeyChecking=no -p " + getAssignedPort()
+ " -v -l cloudbees " + SSH_SERVER_HOST);
job.getBuildersList().add(shell);
Future<? extends FreeStyleBuild> build = job.scheduleBuild2(0);
r.assertBuildStatusSuccess(build);
r.assertLogNotContains("rm: ", build.get());
stopMockSSHServer();
}
@Test
public void sshAgentWithTrickyPassphrase() throws Exception {
startMockSSHServer();
List<String> credentialIds = new ArrayList<String>();
credentialIds.add(CREDENTIAL_ID);
SSHUserPrivateKey key = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, credentialIds.get(0), "cloudbees",
new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(getPrivateKey2()), "* .*", "test");
SystemCredentialsProvider.getInstance().getCredentials().add(key);
SystemCredentialsProvider.getInstance().save();
FreeStyleProject job = r.createFreeStyleProject();
job.setAssignedNode(r.createSlave());
SSHAgentBuildWrapper sshAgent = new SSHAgentBuildWrapper(credentialIds, false);
job.getBuildWrappersList().add(sshAgent);
Shell shell = new Shell("set | grep SSH_AUTH_SOCK "
+ "&& ssh-add -l "
+ "&& ssh -o NoHostAuthenticationForLocalhost=yes -o StrictHostKeyChecking=no -p " + getAssignedPort()
+ " -v -l cloudbees " + SSH_SERVER_HOST);
job.getBuildersList().add(shell);
r.assertBuildStatusSuccess(job.scheduleBuild2(0));
stopMockSSHServer();
}
}